home *** CD-ROM | disk | FTP | other *** search
/ SPACE 1 / SPACE - Library 1 - Volume 1.iso / program / 168 / ss.c < prev    next >
C/C++ Source or Header  |  1988-03-31  |  21KB  |  1,061 lines

  1. /*
  2.  * spread sheet program
  3.  *    commands are copy, read, write, print, blank, format, quit
  4.  *    labels begin with ' and are limited in length
  5.  *    numbers are stored fixed point (2 decimal places) in longs
  6.  */
  7.  
  8. #include "stdio.h"
  9.  
  10. /* sizes */
  11.  
  12. #define MAXROW    100
  13. #define MAXCOL    25
  14. #define MAXLINE    100
  15. #define MAXNODE 5000
  16. #define MAXSTR    16
  17.  
  18. /* screen locations */
  19.  
  20. #define FRAMEW    5
  21. #define FRAMEH    1
  22. #define CURCELL    22
  23. #define MSG    23
  24.  
  25. /* default values for width, format,  and justify */
  26.  
  27. #define DEFWID    10
  28. #define DEFFMT    2
  29. #define DEFJST    'l'
  30.  
  31. /* screen and keyboard defines */
  32.  
  33. #define ESC    27
  34. #define DEL    127
  35. #define HELP    0x6200
  36. #define UNDO    0x6100
  37. #define INS    0x5200
  38. #define CLR    0x4700
  39. #define UP    0x4800
  40. #define DOWN    0x5000
  41. #define LEFT    0x4B00
  42. #define RIGHT    0x4D00
  43. #define BACKSP    8
  44.  
  45. #define LJUST    'l'
  46. #define RJUST    'r'
  47.  
  48. /* cell types */
  49.  
  50. #define FREE    0
  51. #define STRING    1
  52. #define VALUE    2
  53. #define CELL    3
  54. #define ERR    4
  55. #define ADD    '+'
  56. #define SUB    '-'
  57. #define MUL    '*'
  58. #define DIV    '/'
  59. #define NEG    '_'
  60. #define SUM    '@'
  61.  
  62. /* cell structures */
  63.  
  64. typedef struct { int type; long a, b, c, d; }     Node;
  65. typedef struct { int type; char s[MAXSTR]; }     String;
  66. typedef union  { Node n; String s; }         Cell;
  67.  
  68. Cell     *cell[MAXROW][MAXCOL];        /* cell pointers        */
  69. Cell     space[MAXNODE];            /* Cell space            */
  70. Cell     *nextfree;            /* free list of nodes        */
  71. Cell     extra;                /* an extra one when empty    */
  72. int    freecnt;            /* count of free nodes        */
  73.  
  74. char     linebuf[MAXLINE];        /* keyboard input buffer    */
  75. char    showbuf[MAXLINE];        /* buffer for show routine    */
  76.  
  77. char    filename[MAXLINE];        /* load/save file name        */
  78. char    prname[MAXLINE];        /* print file name        */
  79. char    prwin[MAXLINE];            /* print window            */
  80.  
  81. int    crow, ccol;            /* current cursor row and col    */
  82.  
  83. int    frow, lrow;            /* first and last displayed row */
  84. int    fcol, lcol;            /* first and last displayed col    */
  85.  
  86. char    width[MAXCOL];            /* width of the columns        */
  87. char    format[MAXCOL];            /* format of the columns    */
  88. char    justify[MAXCOL];        /* justification of the cols    */
  89. int    loc[MAXCOL];            /* screen location of the cols    */
  90.  
  91. char    *parstr;            /* string for expr parser    */
  92. char    tokstr[MAXLINE];        /* string for next token    */
  93.  
  94. int    reframe;            /* need to reframe        */
  95. int    redisp;                /* need to recalc the display    */
  96.  
  97. main(argc, argv) char *argv[]; {
  98.     int c;
  99.     init();
  100.     if (argc > 1)
  101.         loadss(argv[1]);
  102.     while (1) {
  103.         display();
  104.         switch (c = get()) {
  105.         case 'b': blank(); break;
  106.         case 'c': copy(); break;
  107.         case 'd': delete(); break;
  108.         case 'e': edit(); break;
  109.         case 'f': setformat(); break;
  110.         case 'g': go(); break;
  111.         case 'i': insert(); break;
  112.         case 'l': load(); break;
  113.         case 'p': print(); break;
  114.         case 'q': quit(); break;
  115.         case 's': save(); break;
  116.         case '0': case '1': case '2': case '3': case '4':
  117.         case '5': case '6': case '7': case '8': case '9': 
  118.         case '\'': case '(': case '=':
  119.             enter(c); 
  120.             break;
  121.         case UP: case DOWN: case LEFT: case RIGHT:
  122.             movecur(c);
  123.             break;
  124.         default:
  125.             help();
  126.             break;
  127.         }
  128.     }
  129. }
  130.  
  131. /* initialize the spreadsheet */
  132.  
  133. init() {
  134.     int r, c;
  135.     for (r = 0; r < MAXROW; r++)
  136.         for (c = 0; c < MAXCOL; c++)
  137.             cell[r][c] = NULL;
  138.     crow = ccol = 0;
  139.     frow = fcol = 0;
  140.     reframe = redisp = 1;
  141.     for (c = 0; c < MAXCOL; c++) {
  142.         width[c] = DEFWID;
  143.         format[c] = DEFFMT;
  144.         justify[c] = DEFJST;
  145.     }
  146.     nextfree = NULL;
  147.     freecnt = 0;
  148.     for (r = 0; r < MAXNODE; r++)
  149.         free(&space[r]);
  150.     extra.n.type = ERR;
  151.     erase();
  152. }
  153.  
  154. /* draw a frame on the screen displaying current window rows and cols */
  155.  
  156. frame() {
  157.     int sz, i;
  158.     lrow = frow + 20;
  159.     if (lrow > MAXROW) lrow = MAXROW;
  160.     sz = FRAMEW;
  161.     for (lcol = fcol; lcol < MAXCOL && sz + width[lcol] < 80; lcol++) {
  162.         loc[lcol] = sz;
  163.         sz += width[lcol];
  164.     }
  165.     move(0, FRAMEW);
  166.     reverse(1);
  167.     for (i = fcol; i < lcol; i++) {
  168.         sprintf(linebuf, "%c..............................", 'a'+i);
  169.         outstr(stdout, width[i], LJUST, linebuf);
  170.     }
  171.     for (i = frow; i < lrow; i++) {
  172.         move(FRAMEH + i - frow, 0);
  173.         clrline();
  174.         sprintf(linebuf, "%d.....", i);
  175.         outstr(stdout, FRAMEW, LJUST, linebuf);
  176.     }
  177.     reverse(0);
  178.     clrbelow();
  179.     redisp = 1;
  180. }
  181.  
  182. /* refresh the contents of the current display window */
  183.  
  184. display() {
  185.     int r, c, n;
  186.     Cell *p;
  187.     char *content();
  188.     if (reframe) frame();
  189.     if (redisp) {
  190.         for (r = frow; r < lrow; r++) {
  191.             for (c = fcol; c < lcol; c++) {
  192.                 if (p = cell[r][c]) {
  193.                     move(FRAMEH+r-frow, loc[c]);
  194.                     value(stdout, c, p);
  195.                 }
  196.             }
  197.         }
  198.     }
  199.     move(0, 0); /* how much free space left */
  200.     n = ((long)freecnt * 100L) / (long)MAXNODE;
  201.     sprintf(linebuf, "%d%%", n);
  202.     outstr(stdout, FRAMEW, LJUST, linebuf);
  203.     move(CURCELL, 0); /* what is the content of current cell */
  204.     clrbelow();
  205.     printf("%c%d: %s", ccol+'a', crow, content(cell[crow][ccol], 0));
  206.     move(FRAMEH+crow-frow, loc[ccol]); /* place cursor */
  207.     reframe = redisp = 0;
  208. }
  209.  
  210. /* output the value of the cell */
  211.  
  212. value(fp, c, p) FILE *fp; Cell *p; {
  213.     int i, wid, fmt, jst;
  214.     char *s, *ntoa();
  215.     long n, eval();
  216.     wid = width[c];
  217.     fmt = format[c];
  218.     jst = justify[c];
  219.     if (p == NULL)
  220.         outstr(fp, wid, jst, "");
  221.     else if (p->s.type == STRING)
  222.         outstr(fp, wid, jst, p->s.s);
  223.     else    {
  224.         n = eval(p);
  225.         outstr(fp, wid, jst, ntoa(n, fmt));
  226.     }
  227. }
  228.  
  229. /* get a line from a file */
  230.  
  231. rdline(fp, bp) FILE *fp; char *bp; {
  232.     int i, c;
  233.     for (i = 0; (c = getc(fp)) != '\n' && c != EOF; i++)
  234.         if (c >= ' ')
  235.             *bp++ = c;
  236.     *bp = 0;
  237.     return i;
  238. }
  239.  
  240. /* get a line from the keyboard */
  241.  
  242. char *
  243. getline(prompt, start) char *prompt, *start; {
  244.     int i, j, n, c, plen, change;
  245.     move(MSG, 0);
  246.     clrline();
  247.     for (plen = 2; *prompt; plen++)
  248.         put(*prompt++);
  249.     ps("? ");
  250.     i = 0;
  251.     if (start) {
  252.         while (linebuf[i] = start[i])
  253.             put(linebuf[i++]);
  254.     }
  255.     n = i;
  256.     while ((c = get()) != '\r') {
  257.         change = 0;
  258.         switch (c) {
  259.         case LEFT:
  260.             if (i) i--;
  261.             break;
  262.         case RIGHT:
  263.             if (i < n) i++;
  264.             break;
  265.         case BACKSP:
  266.             if (i == 0) break;
  267.             i--;
  268.         case DEL:
  269.             if (i == n) break;
  270.             n--;
  271.             for (j = i; j < n; j++)
  272.                 linebuf[j] = linebuf[j+1];
  273.             change++;
  274.             break;
  275.         case ESC:
  276.             return NULL;
  277.             break;
  278.         case INS:
  279.             c = ' ';
  280.         default:
  281.             if (c < ' ' || c > DEL) 
  282.                 break;
  283.             for (j = n; j > i; j--)
  284.                 linebuf[j] = linebuf[j-1];
  285.             n++;
  286.             linebuf[i++] = c;
  287.             change++;
  288.             break;
  289.         }
  290.         if (change) {
  291.             j = (i ? i-1 : 0);
  292.             move(MSG, plen+j);
  293.             while (j < n)
  294.                 put(linebuf[j++]);
  295.             put(' ');
  296.         }
  297.         move(MSG, plen+i);
  298.     }
  299.     linebuf[n] = 0;
  300.     return (n ? linebuf : NULL);
  301. }
  302.  
  303. /* move the cursor, if move goes outside the window, adjust frame */
  304.  
  305. movecur(c) {
  306.     switch (c) {
  307.     case UP: 
  308.         if (crow) crow--;
  309.         if (crow < frow) {
  310.             frow--;
  311.             reframe++;
  312.         }
  313.         break;
  314.     case DOWN:
  315.         if (crow < MAXROW-1) crow++;
  316.         if (crow >= lrow) {
  317.             frow++;
  318.             reframe++;
  319.         }
  320.         break;
  321.     case LEFT:
  322.         if (ccol) ccol--;
  323.         if (ccol < fcol) {
  324.             fcol--;
  325.             reframe++;
  326.         }
  327.         break;
  328.     case RIGHT:
  329.         if (ccol < MAXCOL-1) ccol++;
  330.         if (ccol >= lcol) {
  331.             fcol++;
  332.             reframe++;
  333.         }
  334.         break;
  335.     }
  336. }
  337.  
  338. /* time to go */
  339.  
  340. quit() {
  341.     char *s;
  342.     if (s = getline("quit", "yes")) {
  343.         if (*s == 'y') {
  344.             move(24, 0);
  345.             exit(0);
  346.         }
  347.     }
  348. }
  349.  
  350. /* expression entry parsing */
  351.  
  352. char *
  353. next() {
  354.     int c;
  355.     char *s;
  356.     while ((c = *parstr) && c <= ' ')
  357.         parstr++;
  358.     s = tokstr;
  359.     if (alphanum(c)) {
  360.         while (alphanum(*parstr))
  361.             *s++ = *parstr++;
  362.     }
  363.     else    *s++ = *parstr++;
  364.     *s = 0;
  365.     return tokstr;
  366. }
  367.  
  368. /* create a new node */
  369.  
  370. Cell *
  371. mkcell(t, a, b, c, d) long a, b, c, d; {
  372.     Node *p;
  373.     if ((p = nextfree) == NULL) {
  374.         move(MSG, 0);
  375.         printf("out of space");
  376.         return &extra;
  377.     }
  378.     else    {
  379.         freecnt--;
  380.         nextfree = p->a;
  381.         p->type = t;
  382.         p->a = a;
  383.         p->b = b;
  384.         p->c = c;
  385.         p->d = d;
  386.         return p;
  387.     }
  388. }
  389.  
  390. /* parse the string (pointed at by parstr) into an expression tree */
  391.  
  392. Cell *
  393. parse() {
  394.     char *s;
  395.     Cell *x, *factor(), *tail();
  396.     x = factor();
  397.     s = next();
  398.     x = tail(x, *s);
  399. }
  400.  
  401. /* more parsing, here we look for operators */
  402.  
  403. Cell *
  404. tail(x, op) Cell *x; {
  405.     int p;
  406.     char *s;
  407.     Cell *y, *factor();
  408.     while (1) {
  409.         if (!(p = prec(op))) return x;
  410.         y = factor();
  411.         s = next();
  412.         if (prec(*s) > p)
  413.             y = tail(y, *s);
  414.         x = mkcell(op, x, y);
  415.         op = *s;
  416.     }
  417. }
  418.  
  419. /* return the precedence of the given operator */
  420.  
  421. prec(op) {
  422.     switch (op) {
  423.         case '+': case '-': return 1;
  424.         case '*': case '/': return 2;
  425.         case '@': return 3;
  426.         default: return 0;
  427.     }
  428. }
  429.  
  430. /* parse a factor of an expression */
  431.  
  432. Cell *
  433. factor() {
  434.     char *s;
  435.     int i, c, cr, cc;
  436.     long aton();
  437.     Cell *x, *y;
  438.     if (!(s = next())) 
  439.         return NULL;
  440.     switch (c = *s) {
  441.     case '\'':
  442.         x = mkcell(STRING, 0L, 0L);
  443.         s = x->s.s;
  444.         for (i = 0; i < MAXSTR && (*s++ = *parstr++); i++)
  445.             ;
  446.         return x;
  447.     case '-':
  448.         x = factor();
  449.         return mkcell(NEG, x, 0L);
  450.     case '(':
  451.         return parse();
  452.     default:
  453.         if (c >= 'a' && c <= 'z') {
  454.             y = cc = c - 'a';
  455.             x = cr = atoi(s+1);
  456.             return mkcell(
  457.                 check(cr, cc) ? CELL : ERR, x, y);
  458.         }
  459.         else if (c >= '0' && c <= '9') {
  460.             x = aton(s);
  461.             return mkcell(VALUE, x, 0);
  462.         }
  463.         else    {
  464.             move(MSG, 0);
  465.             printf("bad factor: %s", s);
  466.             return NULL;
  467.         }
  468.     }
  469. }
  470.  
  471. /* set the value of a cell */
  472.  
  473. setvalue(r, c, x) long x; {
  474.     Cell *p;
  475.     if (p = cell[r][c])
  476.         free(p);
  477.     cell[r][c] = x;
  478. }
  479.  
  480. /* free up all space associated the given pointer */
  481.  
  482. free(p) Node *p; {
  483.     switch (p->type) {
  484.         case ADD: case SUB: case MUL: case DIV: case SUM:
  485.             free(p->b);
  486.         case NEG:
  487.             free(p->a);
  488.     }
  489.     p->type = FREE;
  490.     p->a = nextfree;
  491.     nextfree = p;
  492.     freecnt++;
  493. }
  494.  
  495. /* get a filename from the user and load a spreadsheet */
  496.  
  497. load() {
  498.     char *s;
  499.     if (s = getline("load file", filename)) {
  500.         loadss(s);
  501.     }
  502. }
  503.  
  504. /* load a spreadsheet file */
  505.  
  506. loadss(name) char *name; {
  507.     FILE *fp;
  508.     int c;
  509.     char *s;
  510.     strcpy(filename, name);
  511.     if (fp = fopen(name, "r")) {
  512.         init();
  513.         while (rdline(fp, parstr = linebuf)) {
  514.             s = next();
  515.             switch (c = *s) {
  516.             case '=':
  517.                 s = next();
  518.                 ccol = *s - 'a';
  519.                 crow = atoi(s+1);
  520.                 if (check(crow, ccol))
  521.                     setvalue(crow, ccol, parse());
  522.                 break;
  523.             case 'f':
  524.                 if (s = next()) {
  525.                     c = atoi(s);
  526.                     if (s = next())
  527.                         width[c] = atoi(s);
  528.                     if (s = next())
  529.                         format[c] = atoi(s);
  530.                     if (s = next())
  531.                         justify[c] = *s;
  532.                 }
  533.                 break;
  534.             }
  535.         }
  536.         reframe = redisp = 1;
  537.         chkframe();
  538.         fclose(fp);
  539.     }
  540.     else    {
  541.         printf(" can't open ", name);
  542.         get();
  543.     }
  544. }
  545.  
  546. /* save a spreadsheet on a file */
  547.  
  548. save() {
  549.     int r, c;
  550.     FILE *fp;
  551.     char *name, *content();
  552.     if (name = getline("save file", filename)) {
  553.         strcpy(filename, name);
  554.         fp = fopen(name, "w");
  555.         for (r = 0; r < MAXROW; r++)
  556.             for (c = 0; c < MAXCOL; c++)
  557.                 if (cell[r][c]) {
  558.                     fprintf(fp, "= %c%d %s\n", 
  559.                         c+'a', r, 
  560.                         content(cell[r][c], 0));
  561.                 }
  562.         for (c = 0; c < MAXCOL; c++)
  563.             if (width[c] != DEFWID || 
  564.                             format[c] != DEFFMT || 
  565.                 justify[c] != DEFJST)
  566.                 fprintf(fp, "f %d %d %d %c\n", 
  567.                     c, width[c], format[c], justify[c]);
  568.         fclose(fp);
  569.     }
  570. }
  571.  
  572. /* print a report onto a file */
  573.  
  574. print() {
  575.     char *s;
  576.     FILE *fp;
  577.     int r, c, trow, tcol, brow, bcol;
  578.     if (!(s = getline("print window", prwin))) return;
  579.     strcpy(prwin, s);
  580.     if (!window(s, &trow, &tcol, &brow, &bcol)) return;
  581.     if (s = getline("file name", NULL)) {
  582.         strcpy(prname, s);
  583.         fp = fopen(s, "w");
  584.         for (r = trow ; r <= brow; r++) {
  585.             for (c = tcol; c <= bcol; c++)
  586.                 value(fp, c, cell[r][c]);
  587.             putc('\n', fp);
  588.         }
  589.         fclose(fp);
  590.     }
  591. }
  592.  
  593. /* prompt for a cell name and move the cursor to that cell */
  594.  
  595. go() {
  596.     char *p;
  597.     if (p = getline("go to cell", NULL)) {
  598.         if (*p >= 'a' && *p <= 'z') {
  599.             ccol = *p - 'a';
  600.             crow = atoi(p+1);
  601.             if (crow < 0) crow = 0;
  602.             if (crow >= MAXROW) crow = MAXROW-1;
  603.             if (ccol < 0) ccol = 0;
  604.             if (ccol >= MAXCOL) ccol = MAXCOL-1;
  605.             chkframe();
  606.         }
  607.     }
  608. }
  609.  
  610. chkframe() {
  611.     if (crow < frow || crow >= lrow ||
  612.             ccol < fcol || ccol >= lcol) {
  613.         frow = crow;
  614.         fcol = ccol;
  615.         reframe = 1;
  616.     }
  617. }
  618.  
  619. /* make a copy of a cell */
  620.  
  621. Cell *
  622. copyx(p, sr, sc, dr, dc) Node *p; {
  623.     int r, c;
  624.     Cell *x, *y;
  625.     if (p == NULL) return NULL;
  626.     switch (p->type) {
  627.         case ADD: case SUB: case MUL: case DIV: case SUM:
  628.             x = copyx(p->a, sr, sc, dr, dc);
  629.             y = copyx(p->b, sr, sc, dr, dc);
  630.             return mkcell(p->type, x, y);
  631.         case CELL:
  632.             r = p->a - sr + dr;
  633.             c = p->b - sc + dc;
  634.             return mkcell(
  635.                 check(r, c) ? CELL : ERR, (long)r, (long)c);
  636.         default:
  637.             return mkcell(p->type, 
  638.                 p->a, p->b, p->c, p->d);
  639.     }
  640. }
  641.  
  642. /* prompt for where to copy to, and then copy it */
  643.  
  644. copy() {
  645.     char *s;
  646.     Cell *src;
  647.     int r, c, trow, tcol, brow, bcol;
  648.     if (!(s = getline("destination", NULL))) 
  649.         return;
  650.     if (!window(s, &trow, &tcol, &brow, &bcol)) 
  651.         return;
  652.     src = cell[crow][ccol];
  653.     for (r = trow; r <= brow; r++) 
  654.         for (c = tcol; c <= bcol; c++)
  655.             setvalue(r, c, copyx(src, crow, ccol, r, c));
  656.     redisp = 1;
  657. }
  658.  
  659. /* insert a row or a column */
  660.  
  661. insert() {
  662.     char *s;
  663.     if (s = getline("insert (row or col)", NULL)) {
  664.         if (*s == 'r')
  665.             insrow();
  666.         else if (*s == 'c')
  667.             inscol();
  668.         else    return;
  669.         reframe = 1;
  670.     }
  671. }
  672.  
  673. insrow() {
  674.     int r, c;
  675.     Cell *p;
  676.     for (r = MAXROW-2; r >= crow; r--) {
  677.         for (c = 0; c < MAXCOL; c++) {
  678.             p = copyx(cell[r][c], r, c, r+1, c);
  679.             setvalue(r+1, c, p);
  680.         }
  681.     }
  682.     for (c = 0; c < MAXCOL; c++)
  683.         setvalue(crow, c, NULL);
  684. }
  685.  
  686. inscol() {
  687.     int r, c;
  688.     Cell *p;
  689.     for (c = MAXCOL-2; c >= ccol; c--) {
  690.         width[c+1] = width[c];
  691.         format[c+1] = format[c];
  692.         justify[c+1] = justify[c];
  693.         for (r = 0; r < MAXROW; r++) {
  694.             p = copyx(cell[r][c], r, c, r, c+1);
  695.             setvalue(r, c+1, p);
  696.         }
  697.     }
  698.     width[ccol] = DEFWID;
  699.     format[ccol] = DEFFMT;
  700.     justify[ccol] = DEFJST;
  701.     for (r = 0; r < MAXROW; r++)
  702.         setvalue(r, ccol, NULL);
  703. }
  704.  
  705. /* delete a row or a column */
  706.  
  707. delete() {
  708.     char *s;
  709.     if (s = getline("delete (row or col)", NULL)) {
  710.         if (*s == 'r')
  711.             delrow();
  712.         else if (*s == 'c')
  713.             delcol();
  714.         else    return;
  715.         reframe = 1;
  716.     }
  717. }
  718.  
  719. delrow() {
  720.     int r, c;
  721.     Cell *p;
  722.     for (r = crow; r < MAXROW-2; r++) {
  723.         for (c = 0; c < MAXCOL; c++) {
  724.             p = copyx(cell[r+1][c], r+1, c, r, c);
  725.             setvalue(r, c, p);
  726.         }
  727.     }
  728. }
  729.  
  730. delcol() {
  731.     int r, c;
  732.     Cell *p;
  733.     for (c = ccol; c < MAXCOL-2; c++) {
  734.         width[c] = width[c+1];
  735.         format[c] = format[c+1];
  736.         justify[c] = justify[c+1];
  737.         for (r = 0; r < MAXROW; r++) {
  738.             p = copyx(cell[r][c+1], r, c+1, r, c);
  739.             setvalue(r, c, p);
  740.         }
  741.     }
  742. }
  743.  
  744. /* enter a new expression into the cell */
  745.  
  746. enter(c) {
  747.     char str[2];
  748.     if (c != '=')
  749.         sprintf(str, "%c", c);
  750.     else    *str = 0;
  751.     if (parstr = getline("enter", str)) {
  752.         setvalue(crow, ccol, parse());
  753.         redisp = 1;
  754.     }
  755. }
  756.  
  757. /* fill the line buffer with the (unevaluated) contents of the cell */
  758.  
  759. char *
  760. content(p, cp) Cell *p; {
  761.     *linebuf = 0;
  762.     showx(p, cp);
  763.     return linebuf;
  764. }
  765.  
  766. /* recursive support routine for content() */
  767.  
  768. showx(p, cp) Node *p; {
  769.     int op, np;
  770.     char *ntoa();
  771.     if (p == NULL) return;
  772.     switch (op = p->type) {
  773.     case ERR:
  774.         sprintf(showbuf, "ERR");
  775.         break;
  776.     case FREE: 
  777.         sprintf(showbuf, "FREE");
  778.         break;
  779.     case STRING: 
  780.         sprintf(showbuf, "'%s", ((String *)p)->s);
  781.         break;
  782.     case CELL: 
  783.         sprintf(showbuf, "%c%d", (short) (p->b + 'a'), (short) p->a); 
  784.         break;
  785.     case VALUE: 
  786.         sprintf(showbuf, "%s", ntoa(p->a, 2));
  787.         break;
  788.     case ADD: case SUB: case MUL: case DIV: case SUM:
  789.         np = prec(op);
  790.         if (np < cp)
  791.             strcat(linebuf, "(");
  792.         showx(p->a, np);
  793.         sprintf(linebuf, "%s%c", linebuf, p->type);
  794.         showx(p->b, np);
  795.         if (np < cp)
  796.             strcat(linebuf, ")");
  797.         *showbuf = 0;
  798.         break;
  799.     case NEG: 
  800.         strcat(linebuf, "-");
  801.         showx(p->a, cp); 
  802.         *showbuf = 0;
  803.         break;
  804.     default:
  805.         sprintf(showbuf, "ERR%d", p->type);
  806.         break;
  807.     }
  808.     strcat(linebuf, showbuf);
  809. }
  810.  
  811. /* erase the current cell */
  812.  
  813. blank() {
  814.     char *s;
  815.     if (s = getline("blank this cell", "yes")) {
  816.         if (*s == 'y') {
  817.             setvalue(crow, ccol, NULL);
  818.             move(FRAMEH+crow-frow, loc[ccol]);
  819.             outstr(stdout, width[ccol], LJUST, "");
  820.         }
  821.     }
  822. }
  823.  
  824. /* edit the contents of the current cell */
  825.  
  826. edit() {
  827.     char *s;
  828.     s = content(cell[crow][ccol], 0);
  829.     if (parstr = getline("edit", s)) {
  830.         setvalue(crow, ccol, parse());
  831.         redisp = 1;
  832.     }
  833. }
  834.  
  835. /* prompt for new column formats and change them */
  836.  
  837. setformat() {
  838.     char *s;
  839.     int w;
  840.     sprintf(linebuf, "%d", format[ccol]);
  841.     if (s = getline("fixed decimal", linebuf)) {
  842.         w = atoi(s);
  843.         format[ccol] = (w > 2 ? 2 : w);
  844.         redisp = 1;
  845.     }
  846.     sprintf(linebuf, "%d", width[ccol]);
  847.     if (s = getline("column width", linebuf)) {
  848.         w = atoi(s);
  849.         width[ccol] = (w < 2 ? 2 : w);
  850.         reframe = 1;
  851.     }
  852.     sprintf(linebuf, "%c", justify[ccol]);
  853.     if (s = getline("left or right justify", linebuf)) {
  854.         if (*s == 'l' || *s == 'r') {
  855.             justify[ccol] = *s;
  856.             reframe = 1;
  857.         }
  858.     }
  859. }
  860.  
  861. /* display a help message */
  862.  
  863. help() { 
  864.     move(MSG, 0);
  865.   printf("cell entry: type \"= expr\" or \"'label\" ");
  866.       move(MSG+1, 0);
  867.     printf(
  868.   "commands: blank copy delete edit format goto insert load print quit save");
  869.     get();
  870. }
  871.  
  872. /* evaluate a cell entry */
  873.  
  874. long
  875. eval(p) Node *p; {
  876.     int x, y;
  877.     long a, b, sum();
  878.     if (p == NULL) return 0L;
  879.     switch (p->type) {
  880.     case FREE: case STRING: case ERR:
  881.         return 0L;
  882.     case VALUE:
  883.         return p->a;
  884.     case CELL:
  885.         x = p->a;
  886.         y = p->b;
  887.         return eval(cell[x][y]);
  888.     case SUM:
  889.         return sum(p->a, p->b);
  890.         break;
  891.     case ADD:
  892.         a = eval(p->a);
  893.         b = eval(p->b);
  894.         return a+b;
  895.     case SUB:
  896.         a = eval(p->a);
  897.         b = eval(p->b);
  898.         return a-b;
  899.     case MUL:
  900.         a = eval(p->a);
  901.         b = eval(p->b);
  902.         return (a*b)/100;
  903.     case DIV:
  904.         a = eval(p->a);
  905.         b = eval(p->b);
  906.         return (a*100)/b;
  907.     case NEG:
  908.         a = eval(p->a);
  909.         return -a;
  910.     }
  911. }
  912.  
  913. long
  914. sum(lp, rp) Node *lp, *rp; {
  915.     long n;
  916.     int br, bc, er, ec, r, c;
  917.     n = 0L;
  918.     if (lp->type == CELL && rp->type == CELL) {
  919.         br = lp->a;
  920.         bc = lp->b;
  921.         er = rp->a;
  922.         ec = rp->b;
  923.         for (r = br; r <= er; r++)
  924.             for (c = bc; c <= ec; c++)
  925.                 n += eval(cell[r][c]);
  926.     }
  927.     return n;
  928. }
  929.  
  930. /* parse a window definition, e.g. "a0.z99" */
  931.  
  932. window(s, tr, tc, br, bc) char *s; int *tr, *tc, *br, *bc; {
  933.     *tc = *s - 'a';
  934.     *tr = atoi(s+1);
  935.     while (*s && *s++ != '.')
  936.         ;
  937.     if (*s) {
  938.         *bc = *s - 'a';
  939.         *br = atoi(s+1);
  940.     }
  941.     else    {
  942.         *bc = *tc;
  943.         *br = *tr;
  944.     }
  945.     return check(*tr, *tc) && check(*br, *bc);
  946. }
  947.  
  948. /* range check on a row and column */
  949.  
  950. check(r, c) { return r >= 0 && r < MAXROW && c >= 0 && c < MAXCOL; }
  951.  
  952. /* convert a string to an integer */
  953.  
  954. atoi(s) char *s; {
  955.     int n, c;
  956.     if (s == NULL) return 0;
  957.     for (n = 0; isdig(c = *s++); )
  958.         n = n * 10 + c - '0';
  959.     return n;
  960. }
  961.  
  962. /* convert a string to a "fixed point" number (i.e. "123.45") */
  963.  
  964. long
  965. aton(s) char *s; {
  966.     int c, i;
  967.     long n;
  968.     n = 0L;
  969.     if (s == NULL) return 0L;
  970.     for (n = 0; isdig(c = *s++); )
  971.         n = n * 10 + (c - '0');
  972.     if (c == '.') c = *s++;
  973.     for (i = 0; i < 2; i++) {
  974.         n = n * 10;
  975.         if (isdig(c)) {
  976.             n = n + (c - '0');
  977.             c = *s++;
  978.         }
  979.     }
  980.     return n;
  981. }
  982.  
  983. /* check the types of the characters */
  984.  
  985. isdig(c) { return c >= '0' && c <= '9'; }
  986.  
  987. alphanum(c) {
  988.     if (c >= 'a' && c <= 'z') return 1;
  989.     if (c >= 'A' && c <= 'Z') return 1;
  990.     if (c == '.') return 1;
  991.     return isdig(c);
  992. }
  993.  
  994. /* convert the fixed point number back to a string */
  995.  
  996. char *
  997. ntoa(n, fmt) long n; {
  998.     int i, neg;
  999.     if (neg = n < 0)
  1000.         n = -n;
  1001.     i = 16;
  1002.     linebuf[--i] = 0;
  1003.     do    {
  1004.         linebuf[--i] = n % 10 + '0';
  1005.         n = n / 10;
  1006.         if (i == 13) linebuf[--i] = '.';
  1007.         } while (n);
  1008.     while (i > 11) {
  1009.         if (i == 13) linebuf[--i] = '.';
  1010.         else linebuf[--i] = '0';
  1011.     }
  1012.     if (fmt == 0) 
  1013.         linebuf[12] = 0;
  1014.     else if (fmt == 1) 
  1015.         linebuf[14] = 0;
  1016.     if (neg) linebuf[--i] = '-';
  1017.     return &linebuf[i];
  1018. }
  1019.  
  1020. /* output a string of width n to the stream fp */
  1021.  
  1022. outstr(fp, wid, jst, s) FILE *fp; char *s; {
  1023.     int i;
  1024.     if (jst == RJUST) {
  1025.         for (i = 0; s[i]; i++)
  1026.             ;
  1027.         for ( ; i < wid; i++)
  1028.             putc(' ', fp);
  1029.     }
  1030.     for (i = 0; i < wid && *s; i++)
  1031.         putc(*s++, fp);
  1032.     if (jst == LJUST) {
  1033.         for ( ; i < wid; i++)
  1034.             putc(' ', fp);
  1035.     }
  1036. }
  1037.  
  1038. /*  keyboard input and screen output/control */
  1039.  
  1040. get() {
  1041.     long c;
  1042.     c = trap(1, 7);
  1043.     if (c & 0xFF) 
  1044.         return (short)c; /* ascii character */
  1045.     else     return (short)(c >> 8); /* scan code */
  1046. }
  1047.  
  1048. move(row, col) { put(ESC); put('Y'); put(row+' '); put(col+' '); }
  1049.  
  1050. erase() { put(ESC); put('E'); }
  1051.  
  1052. clrline() { put(ESC); put('K'); }
  1053.  
  1054. clrbelow() { put(ESC); put('J'); }
  1055.     
  1056. reverse(on) { put(ESC); put(on ? 'p' : 'q'); }
  1057.  
  1058. put(c) { trap(1, 2, c); }
  1059.  
  1060. ps(s) char *s; { while (*s) put(*s++); }
  1061.